home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkScrollbar.c < prev    next >
C/C++ Source or Header  |  1995-06-29  |  40KB  |  1,229 lines

  1. /* 
  2.  * tkScrollbar.c --
  3.  *
  4.  *    This module implements a scrollbar widgets for the Tk
  5.  *    toolkit.  A scrollbar displays a slider and two arrows;
  6.  *    mouse clicks on features within the scrollbar cause
  7.  *    scrolling commands to be invoked.
  8.  *
  9.  * Copyright (c) 1990-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  */
  15.  
  16. static char sccsid[] = "@(#) tkScrollbar.c 1.74 95/06/29 13:59:50";
  17.  
  18. #include "tkPort.h"
  19. #include "default.h"
  20. #include "tkInt.h"
  21.  
  22. /*
  23.  * A data structure of the following type is kept for each scrollbar
  24.  * widget managed by this file:
  25.  */
  26.  
  27. typedef struct {
  28.     Tk_Window tkwin;        /* Window that embodies the scrollbar.  NULL
  29.                  * means that the window has been destroyed
  30.                  * but the data structures haven't yet been
  31.                  * cleaned up.*/
  32.     Display *display;        /* Display containing widget.  Used, among
  33.                  * other things, so that resources can be
  34.                  * freed even after tkwin has gone away. */
  35.     Tcl_Interp *interp;        /* Interpreter associated with scrollbar. */
  36.     Tcl_Command widgetCmd;    /* Token for scrollbar's widget command. */
  37.     Tk_Uid orientUid;        /* Orientation for window ("vertical" or
  38.                  * "horizontal"). */
  39.     int vertical;        /* Non-zero means vertical orientation
  40.                  * requested, zero means horizontal. */
  41.     int width;            /* Desired narrow dimension of scrollbar,
  42.                  * in pixels. */
  43.     char *command;        /* Command prefix to use when invoking
  44.                  * scrolling commands.  NULL means don't
  45.                  * invoke commands.  Malloc'ed. */
  46.     int commandSize;        /* Number of non-NULL bytes in command. */
  47.     int repeatDelay;        /* How long to wait before auto-repeating
  48.                  * on scrolling actions (in ms). */
  49.     int repeatInterval;        /* Interval between autorepeats (in ms). */
  50.     int jump;            /* Value of -jump option. */
  51.  
  52.     /*
  53.      * Information used when displaying widget:
  54.      */
  55.  
  56.     int borderWidth;        /* Width of 3-D borders. */
  57.     Tk_3DBorder bgBorder;    /* Used for drawing background (all flat
  58.                  * surfaces except for trough). */
  59.     Tk_3DBorder activeBorder;    /* For drawing backgrounds when active (i.e.
  60.                  * when mouse is positioned over element). */
  61.     XColor *troughColorPtr;    /* Color for drawing trough. */
  62.     GC troughGC;        /* For drawing trough. */
  63.     GC copyGC;            /* Used for copying from pixmap onto screen. */
  64.     int relief;            /* Indicates whether window as a whole is
  65.                  * raised, sunken, or flat. */
  66.     int highlightWidth;        /* Width in pixels of highlight to draw
  67.                  * around widget when it has the focus.
  68.                  * <= 0 means don't draw a highlight. */
  69.     XColor *highlightBgColorPtr;
  70.                 /* Color for drawing traversal highlight
  71.                  * area when highlight is off. */
  72.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  73.     int inset;            /* Total width of all borders, including
  74.                  * traversal highlight and 3-D border.
  75.                  * Indicates how much interior stuff must
  76.                  * be offset from outside edges to leave
  77.                  * room for borders. */
  78.     int elementBorderWidth;    /* Width of border to draw around elements
  79.                  * inside scrollbar (arrows and slider).
  80.                  * -1 means use borderWidth. */
  81.     int arrowLength;        /* Length of arrows along long dimension of
  82.                  * scrollbar, including space for a small gap
  83.                  * between the arrow and the slider.
  84.                  * Recomputed on window size changes. */
  85.     int sliderFirst;        /* Pixel coordinate of top or left edge
  86.                  * of slider area, including border. */
  87.     int sliderLast;        /* Coordinate of pixel just after bottom
  88.                  * or right edge of slider area, including
  89.                  * border. */
  90.     int activeField;        /* Names field to be displayed in active
  91.                  * colors, such as TOP_ARROW, or 0 for
  92.                  * no field. */
  93.     int activeRelief;        /* Value of -activeRelief option: relief
  94.                  * to use for active element. */
  95.  
  96.     /*
  97.      * Information describing the application related to the scrollbar.
  98.      * This information is provided by the application by invoking the
  99.      * "set" widget command.  This information can now be provided in
  100.      * two ways:  the "old" form (totalUnits, windowUnits, firstUnit,
  101.      * and lastUnit), or the "new" form (firstFraction and lastFraction).
  102.      * FirstFraction and lastFraction will always be valid, but
  103.      * the old-style information is only valid if the NEW_STYLE_COMMANDS
  104.      * flag is 0.
  105.      */
  106.  
  107.     int totalUnits;        /* Total dimension of application, in
  108.                  * units.  Valid only if the NEW_STYLE_COMMANDS
  109.                  * flag isn't set. */
  110.     int windowUnits;        /* Maximum number of units that can be
  111.                  * displayed in the window at once.  Valid
  112.                  * only if the NEW_STYLE_COMMANDS flag isn't
  113.                  * set. */
  114.     int firstUnit;        /* Number of last unit visible in
  115.                  * application's window.  Valid only if the
  116.                  * NEW_STYLE_COMMANDS flag isn't set. */
  117.     int lastUnit;        /* Index of last unit visible in window.
  118.                  * Valid only if the NEW_STYLE_COMMANDS
  119.                  * flag isn't set. */
  120.     double firstFraction;    /* Position of first visible thing in window,
  121.                  * specified as a fraction between 0 and
  122.                  * 1.0. */
  123.     double lastFraction;    /* Position of last visible thing in window,
  124.                  * specified as a fraction between 0 and
  125.                  * 1.0. */
  126.  
  127.     /*
  128.      * Miscellaneous information:
  129.      */
  130.  
  131.     Cursor cursor;        /* Current cursor for window, or None. */
  132.     char *takeFocus;        /* Value of -takefocus option;  not used in
  133.                  * the C code, but used by keyboard traversal
  134.                  * scripts.  Malloc'ed, but may be NULL. */
  135.     Tk_TimerToken autoRepeat;    /* Token for auto-repeat that's
  136.                  * currently in progress.  NULL means no
  137.                  * auto-repeat in progress. */
  138.     int flags;            /* Various flags;  see below for
  139.                  * definitions. */
  140. } Scrollbar;
  141.  
  142. /*
  143.  * Legal values for "activeField" field of Scrollbar structures.  These
  144.  * are also the return values from the ScrollbarPosition procedure.
  145.  */
  146.  
  147. #define OUTSIDE        0
  148. #define TOP_ARROW    1
  149. #define TOP_GAP        2
  150. #define SLIDER        3
  151. #define BOTTOM_GAP    4
  152. #define BOTTOM_ARROW    5
  153.  
  154. /*
  155.  * Flag bits for scrollbars:
  156.  * 
  157.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  158.  *                has already been queued to redraw
  159.  *                this window.
  160.  * NEW_STYLE_COMMANDS:        Non-zero means the new style of commands
  161.  *                should be used to communicate with the
  162.  *                widget:  ".t yview scroll 2 lines", instead
  163.  *                of ".t yview 40", for example.
  164.  * GOT_FOCUS:            Non-zero means this window has the input
  165.  *                focus.
  166.  */
  167.  
  168. #define REDRAW_PENDING        1
  169. #define NEW_STYLE_COMMANDS    2
  170. #define GOT_FOCUS        4
  171.  
  172. /*
  173.  * Minimum slider length, in pixels (designed to make sure that the slider
  174.  * is always easy to grab with the mouse).
  175.  */
  176.  
  177. #define MIN_SLIDER_LENGTH    5
  178.  
  179. /*
  180.  * Information used for argv parsing.
  181.  */
  182.  
  183. static Tk_ConfigSpec configSpecs[] = {
  184.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  185.     DEF_SCROLLBAR_ACTIVE_BG_COLOR, Tk_Offset(Scrollbar, activeBorder),
  186.     TK_CONFIG_COLOR_ONLY},
  187.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  188.     DEF_SCROLLBAR_ACTIVE_BG_MONO, Tk_Offset(Scrollbar, activeBorder),
  189.     TK_CONFIG_MONO_ONLY},
  190.     {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
  191.     DEF_SCROLLBAR_ACTIVE_RELIEF, Tk_Offset(Scrollbar, activeRelief), 0},
  192.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  193.     DEF_SCROLLBAR_BG_COLOR, Tk_Offset(Scrollbar, bgBorder),
  194.     TK_CONFIG_COLOR_ONLY},
  195.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  196.     DEF_SCROLLBAR_BG_MONO, Tk_Offset(Scrollbar, bgBorder),
  197.     TK_CONFIG_MONO_ONLY},
  198.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  199.     (char *) NULL, 0, 0},
  200.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  201.     (char *) NULL, 0, 0},
  202.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  203.     DEF_SCROLLBAR_BORDER_WIDTH, Tk_Offset(Scrollbar, borderWidth), 0},
  204.     {TK_CONFIG_STRING, "-command", "command", "Command",
  205.     DEF_SCROLLBAR_COMMAND, Tk_Offset(Scrollbar, command),
  206.     TK_CONFIG_NULL_OK},
  207.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  208.     DEF_SCROLLBAR_CURSOR, Tk_Offset(Scrollbar, cursor), TK_CONFIG_NULL_OK},
  209.     {TK_CONFIG_PIXELS, "-elementborderwidth", "elementBorderWidth",
  210.     "BorderWidth", DEF_SCROLLBAR_EL_BORDER_WIDTH,
  211.     Tk_Offset(Scrollbar, elementBorderWidth), 0},
  212.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  213.     "HighlightBackground", DEF_SCROLLBAR_HIGHLIGHT_BG,
  214.     Tk_Offset(Scrollbar, highlightBgColorPtr), 0},
  215.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  216.     DEF_SCROLLBAR_HIGHLIGHT,
  217.     Tk_Offset(Scrollbar, highlightColorPtr), 0},
  218.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  219.     "HighlightThickness",
  220.     DEF_SCROLLBAR_HIGHLIGHT_WIDTH, Tk_Offset(Scrollbar, highlightWidth), 0},
  221.     {TK_CONFIG_BOOLEAN, "-jump", "jump", "Jump",
  222.     DEF_SCROLLBAR_JUMP, Tk_Offset(Scrollbar, jump), 0},
  223.     {TK_CONFIG_UID, "-orient", "orient", "Orient",
  224.     DEF_SCROLLBAR_ORIENT, Tk_Offset(Scrollbar, orientUid), 0},
  225.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  226.     DEF_SCROLLBAR_RELIEF, Tk_Offset(Scrollbar, relief), 0},
  227.     {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
  228.     DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(Scrollbar, repeatDelay), 0},
  229.     {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
  230.     DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(Scrollbar, repeatInterval), 0},
  231.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  232.     DEF_SCROLLBAR_TAKE_FOCUS, Tk_Offset(Scrollbar, takeFocus),
  233.     TK_CONFIG_NULL_OK},
  234.     {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
  235.     DEF_SCROLLBAR_TROUGH_COLOR, Tk_Offset(Scrollbar, troughColorPtr),
  236.     TK_CONFIG_COLOR_ONLY},
  237.     {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
  238.     DEF_SCROLLBAR_TROUGH_MONO, Tk_Offset(Scrollbar, troughColorPtr),
  239.     TK_CONFIG_MONO_ONLY},
  240.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  241.     DEF_SCROLLBAR_WIDTH, Tk_Offset(Scrollbar, width), 0},
  242.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  243.     (char *) NULL, 0, 0}
  244. };
  245.  
  246. /*
  247.  * Forward declarations for procedures defined later in this file:
  248.  */
  249.  
  250. static void        ComputeScrollbarGeometry _ANSI_ARGS_((
  251.                 Scrollbar *scrollPtr));
  252. static int        ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
  253.                 Scrollbar *scrollPtr, int argc, char **argv,
  254.                 int flags));
  255. static void        DestroyScrollbar _ANSI_ARGS_((ClientData clientData));
  256. static void        DisplayScrollbar _ANSI_ARGS_((ClientData clientData));
  257. static void        EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr));
  258. static void        ScrollbarCmdDeletedProc _ANSI_ARGS_((
  259.                 ClientData clientData));
  260. static void        ScrollbarEventProc _ANSI_ARGS_((ClientData clientData,
  261.                 XEvent *eventPtr));
  262. static int        ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr,
  263.                 int x, int y));
  264. static int        ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
  265.                 Tcl_Interp *, int argc, char **argv));
  266.  
  267. /*
  268.  *--------------------------------------------------------------
  269.  *
  270.  * Tk_ScrollbarCmd --
  271.  *
  272.  *    This procedure is invoked to process the "scrollbar" Tcl
  273.  *    command.  See the user documentation for details on what
  274.  *    it does.
  275.  *
  276.  * Results:
  277.  *    A standard Tcl result.
  278.  *
  279.  * Side effects:
  280.  *    See the user documentation.
  281.  *
  282.  *--------------------------------------------------------------
  283.  */
  284.  
  285. int
  286. Tk_ScrollbarCmd(clientData, interp, argc, argv)
  287.     ClientData clientData;    /* Main window associated with
  288.                  * interpreter. */
  289.     Tcl_Interp *interp;        /* Current interpreter. */
  290.     int argc;            /* Number of arguments. */
  291.     char **argv;        /* Argument strings. */
  292. {
  293.     Tk_Window tkwin = (Tk_Window) clientData;
  294.     register Scrollbar *scrollPtr;
  295.     Tk_Window new;
  296.  
  297.     if (argc < 2) {
  298.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  299.         argv[0], " pathName ?options?\"", (char *) NULL);
  300.     return TCL_ERROR;
  301.     }
  302.  
  303.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  304.     if (new == NULL) {
  305.     return TCL_ERROR;
  306.     }
  307.  
  308.     /*
  309.      * Initialize fields that won't be initialized by ConfigureScrollbar,
  310.      * or which ConfigureScrollbar expects to have reasonable values
  311.      * (e.g. resource pointers).
  312.      */
  313.  
  314.     scrollPtr = (Scrollbar *) ckalloc(sizeof(Scrollbar));
  315.     scrollPtr->tkwin = new;
  316.     scrollPtr->display = Tk_Display(new);
  317.     scrollPtr->interp = interp;
  318.     scrollPtr->widgetCmd = Tcl_CreateCommand(interp,
  319.         Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
  320.         (ClientData) scrollPtr, ScrollbarCmdDeletedProc);
  321.     scrollPtr->orientUid = NULL;
  322.     scrollPtr->vertical = 0;
  323.     scrollPtr->width = 0;
  324.     scrollPtr->command = NULL;
  325.     scrollPtr->commandSize = 0;
  326.     scrollPtr->repeatDelay = 0;
  327.     scrollPtr->repeatInterval = 0;
  328.     scrollPtr->borderWidth = 0;
  329.     scrollPtr->bgBorder = NULL;
  330.     scrollPtr->activeBorder = NULL;
  331.     scrollPtr->troughColorPtr = NULL;
  332.     scrollPtr->troughGC = None;
  333.     scrollPtr->copyGC = None;
  334.     scrollPtr->relief = TK_RELIEF_FLAT;
  335.     scrollPtr->highlightWidth = 0;
  336.     scrollPtr->highlightBgColorPtr = NULL;
  337.     scrollPtr->highlightColorPtr = NULL;
  338.     scrollPtr->inset = 0;
  339.     scrollPtr->elementBorderWidth = -1;
  340.     scrollPtr->arrowLength = 0;
  341.     scrollPtr->sliderFirst = 0;
  342.     scrollPtr->sliderLast = 0;
  343.     scrollPtr->activeField = 0;
  344.     scrollPtr->activeRelief = TK_RELIEF_RAISED;
  345.     scrollPtr->totalUnits = 0;
  346.     scrollPtr->windowUnits = 0;
  347.     scrollPtr->firstUnit = 0;
  348.     scrollPtr->lastUnit = 0;
  349.     scrollPtr->firstFraction = 0.0;
  350.     scrollPtr->lastFraction = 0.0;
  351.     scrollPtr->cursor = None;
  352.     scrollPtr->takeFocus = NULL;
  353.     scrollPtr->autoRepeat = NULL;
  354.     scrollPtr->flags = 0;
  355.  
  356.     Tk_SetClass(scrollPtr->tkwin, "Scrollbar");
  357.     Tk_CreateEventHandler(scrollPtr->tkwin,
  358.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  359.         ScrollbarEventProc, (ClientData) scrollPtr);
  360.     if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
  361.     goto error;
  362.     }
  363.  
  364.     interp->result = Tk_PathName(scrollPtr->tkwin);
  365.     return TCL_OK;
  366.  
  367.     error:
  368.     Tk_DestroyWindow(scrollPtr->tkwin);
  369.     return TCL_ERROR;
  370. }
  371.  
  372. /*
  373.  *--------------------------------------------------------------
  374.  *
  375.  * ScrollbarWidgetCmd --
  376.  *
  377.  *    This procedure is invoked to process the Tcl command
  378.  *    that corresponds to a widget managed by this module.
  379.  *    See the user documentation for details on what it does.
  380.  *
  381.  * Results:
  382.  *    A standard Tcl result.
  383.  *
  384.  * Side effects:
  385.  *    See the user documentation.
  386.  *
  387.  *--------------------------------------------------------------
  388.  */
  389.  
  390. static int
  391. ScrollbarWidgetCmd(clientData, interp, argc, argv)
  392.     ClientData clientData;    /* Information about scrollbar
  393.                      * widget. */
  394.     Tcl_Interp *interp;            /* Current interpreter. */
  395.     int argc;                /* Number of arguments. */
  396.     char **argv;            /* Argument strings. */
  397. {
  398.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  399.     int result = TCL_OK;
  400.     size_t length;
  401.     int c;
  402.  
  403.     if (argc < 2) {
  404.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  405.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  406.     return TCL_ERROR;
  407.     }
  408.     Tk_Preserve((ClientData) scrollPtr);
  409.     c = argv[1][0];
  410.     length = strlen(argv[1]);
  411.     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
  412.     if (argc == 2) {
  413.         switch (scrollPtr->activeField) {
  414.         case TOP_ARROW:        interp->result = "arrow1";    break;
  415.         case SLIDER:        interp->result = "slider";    break;
  416.         case BOTTOM_ARROW:    interp->result = "arrow2";    break;
  417.         }
  418.         goto done;
  419.     }
  420.     if (argc != 3) {
  421.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  422.             argv[0], " activate element\"", (char *) NULL);
  423.         goto error;
  424.     }
  425.     c = argv[2][0];
  426.     length = strlen(argv[2]);
  427.     if ((c == 'a') && (strcmp(argv[2], "arrow1") == 0)) {
  428.         scrollPtr->activeField = TOP_ARROW;
  429.     } else if ((c == 'a') && (strcmp(argv[2], "arrow2") == 0)) {
  430.         scrollPtr->activeField = BOTTOM_ARROW;
  431.     } else if ((c == 's') && (strncmp(argv[2], "slider", length) == 0)) {
  432.         scrollPtr->activeField = SLIDER;
  433.     } else {
  434.         scrollPtr->activeField = OUTSIDE;
  435.     }
  436.     EventuallyRedraw(scrollPtr);
  437.     } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  438.         && (length >= 2)) {
  439.     if (argc != 3) {
  440.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  441.             argv[0], " cget option\"",
  442.             (char *) NULL);
  443.         goto error;
  444.     }
  445.     result = Tk_ConfigureValue(interp, scrollPtr->tkwin, configSpecs,
  446.         (char *) scrollPtr, argv[2], 0);
  447.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  448.         && (length >= 2)) {
  449.     if (argc == 2) {
  450.         result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
  451.             (char *) scrollPtr, (char *) NULL, 0);
  452.     } else if (argc == 3) {
  453.         result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
  454.             (char *) scrollPtr, argv[2], 0);
  455.     } else {
  456.         result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
  457.             TK_CONFIG_ARGV_ONLY);
  458.     }
  459.     } else if ((c == 'd') && (strncmp(argv[1], "delta", length) == 0)) {
  460.     int xDelta, yDelta, pixels, length;
  461.     double fraction;
  462.  
  463.     if (argc != 4) {
  464.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  465.             argv[0], " delta xDelta yDelta\"", (char *) NULL);
  466.         goto error;
  467.     }
  468.     if ((Tcl_GetInt(interp, argv[2], &xDelta) != TCL_OK)
  469.         || (Tcl_GetInt(interp, argv[3], &yDelta) != TCL_OK)) {
  470.         goto error;
  471.     }
  472.     if (scrollPtr->vertical) {
  473.         pixels = yDelta;
  474.         length = Tk_Height(scrollPtr->tkwin) - 1
  475.             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  476.     } else {
  477.         pixels = xDelta;
  478.         length = Tk_Width(scrollPtr->tkwin) - 1
  479.             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  480.     }
  481.     if (length == 0) {
  482.         fraction = 0.0;
  483.     } else {
  484.         fraction = ((double) pixels / (double) length);
  485.     }
  486.     sprintf(interp->result, "%g", fraction);
  487.     } else if ((c == 'f') && (strncmp(argv[1], "fraction", length) == 0)) {
  488.     int x, y, pos, length;
  489.     double fraction;
  490.  
  491.     if (argc != 4) {
  492.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  493.             argv[0], " fraction x y\"", (char *) NULL);
  494.         goto error;
  495.     }
  496.     if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
  497.         || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
  498.         goto error;
  499.     }
  500.     if (scrollPtr->vertical) {
  501.         pos = y - (scrollPtr->arrowLength + scrollPtr->inset);
  502.         length = Tk_Height(scrollPtr->tkwin) - 1
  503.             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  504.     } else {
  505.         pos = x - (scrollPtr->arrowLength + scrollPtr->inset);
  506.         length = Tk_Width(scrollPtr->tkwin) - 1
  507.             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  508.     }
  509.     if (length == 0) {
  510.         fraction = 0.0;
  511.     } else {
  512.         fraction = ((double) pos / (double) length);
  513.     }
  514.     if (fraction < 0) {
  515.         fraction = 0;
  516.     } else if (fraction > 1.0) {
  517.         fraction = 1.0;
  518.     }
  519.     sprintf(interp->result, "%g", fraction);
  520.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  521.     if (argc != 2) {
  522.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  523.             argv[0], " get\"", (char *) NULL);
  524.         goto error;
  525.     }
  526.     if (scrollPtr->flags & NEW_STYLE_COMMANDS) {
  527.         char first[TCL_DOUBLE_SPACE], last[TCL_DOUBLE_SPACE];
  528.  
  529.         Tcl_PrintDouble(interp, scrollPtr->firstFraction, first);
  530.         Tcl_PrintDouble(interp, scrollPtr->lastFraction, last);
  531.         Tcl_AppendResult(interp, first, " ", last, (char *) NULL);
  532.     } else {
  533.         sprintf(interp->result, "%d %d %d %d", scrollPtr->totalUnits,
  534.             scrollPtr->windowUnits, scrollPtr->firstUnit,
  535.             scrollPtr->lastUnit);
  536.     }
  537.     } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
  538.     int x, y, thing;
  539.  
  540.     if (argc != 4) {
  541.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  542.             argv[0], " identify x y\"", (char *) NULL);
  543.         goto error;
  544.     }
  545.     if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
  546.         || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
  547.         goto error;
  548.     }
  549.     thing = ScrollbarPosition(scrollPtr, x,y);
  550.     switch (thing) {
  551.         case TOP_ARROW:    interp->result = "arrow1";    break;
  552.         case TOP_GAP:    interp->result = "trough1";    break;
  553.         case SLIDER:    interp->result = "slider";    break;
  554.         case BOTTOM_GAP:    interp->result = "trough2";    break;
  555.         case BOTTOM_ARROW:    interp->result = "arrow2";    break;
  556.     }
  557.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
  558.     int totalUnits, windowUnits, firstUnit, lastUnit;
  559.  
  560.     if (argc == 4) {
  561.         double first, last;
  562.  
  563.         if (Tcl_GetDouble(interp, argv[2], &first) != TCL_OK) {
  564.         goto error;
  565.         }
  566.         if (Tcl_GetDouble(interp, argv[3], &last) != TCL_OK) {
  567.         goto error;
  568.         }
  569.         if (first < 0) {
  570.         scrollPtr->firstFraction = 0;
  571.         } else if (first > 1.0) {
  572.         scrollPtr->firstFraction = 1.0;
  573.         } else {
  574.         scrollPtr->firstFraction = first;
  575.         }
  576.         if (last < scrollPtr->firstFraction) {
  577.         scrollPtr->lastFraction = scrollPtr->firstFraction;
  578.         } else if (last > 1.0) {
  579.         scrollPtr->lastFraction = 1.0;
  580.         } else {
  581.         scrollPtr->lastFraction = last;
  582.         }
  583.         scrollPtr->flags |= NEW_STYLE_COMMANDS;
  584.     } else if (argc == 6) {
  585.         if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
  586.         goto error;
  587.         }
  588.         if (totalUnits < 0) {
  589.         totalUnits = 0;
  590.         }
  591.         if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
  592.         goto error;
  593.         }
  594.         if (windowUnits < 0) {
  595.         windowUnits = 0;
  596.         }
  597.         if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
  598.         goto error;
  599.         }
  600.         if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
  601.         goto error;
  602.         }
  603.         if (totalUnits > 0) {
  604.         if (lastUnit < firstUnit) {
  605.             lastUnit = firstUnit;
  606.         }
  607.         } else {
  608.         firstUnit = lastUnit = 0;
  609.         }
  610.         scrollPtr->totalUnits = totalUnits;
  611.         scrollPtr->windowUnits = windowUnits;
  612.         scrollPtr->firstUnit = firstUnit;
  613.         scrollPtr->lastUnit = lastUnit;
  614.         if (scrollPtr->totalUnits == 0) {
  615.         scrollPtr->firstFraction = 0.0;
  616.         scrollPtr->lastFraction = 1.0;
  617.         } else {
  618.         scrollPtr->firstFraction = ((double) firstUnit)/totalUnits;
  619.         scrollPtr->lastFraction = ((double) (lastUnit+1))/totalUnits;
  620.         }
  621.         scrollPtr->flags &= ~NEW_STYLE_COMMANDS;
  622.     } else {
  623.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  624.             argv[0], " set firstFraction lastFraction\" or \"",
  625.             argv[0],
  626.             " set totalUnits windowUnits firstUnit lastUnit\"",
  627.             (char *) NULL);
  628.         goto error;
  629.     }
  630.     ComputeScrollbarGeometry(scrollPtr);
  631.     EventuallyRedraw(scrollPtr);
  632.     } else {
  633.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  634.         "\": must be activate, cget, configure, delta, fraction, ",
  635.         "get, identify, or set", (char *) NULL);
  636.     goto error;
  637.     }
  638.     done:
  639.     Tk_Release((ClientData) scrollPtr);
  640.     return result;
  641.  
  642.     error:
  643.     Tk_Release((ClientData) scrollPtr);
  644.     return TCL_ERROR;
  645. }
  646.  
  647. /*
  648.  *----------------------------------------------------------------------
  649.  *
  650.  * DestroyScrollbar --
  651.  *
  652.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  653.  *    to clean up the internal structure of a scrollbar at a safe time
  654.  *    (when no-one is using it anymore).
  655.  *
  656.  * Results:
  657.  *    None.
  658.  *
  659.  * Side effects:
  660.  *    Everything associated with the scrollbar is freed up.
  661.  *
  662.  *----------------------------------------------------------------------
  663.  */
  664.  
  665. static void
  666. DestroyScrollbar(clientData)
  667.     ClientData clientData;    /* Info about scrollbar widget. */
  668. {
  669.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  670.  
  671.     /*
  672.      * Free up all the stuff that requires special handling, then
  673.      * let Tk_FreeOptions handle all the standard option-related
  674.      * stuff.
  675.      */
  676.  
  677.     if (scrollPtr->troughGC != None) {
  678.     Tk_FreeGC(scrollPtr->display, scrollPtr->troughGC);
  679.     }
  680.     if (scrollPtr->copyGC != None) {
  681.     Tk_FreeGC(scrollPtr->display, scrollPtr->copyGC);
  682.     }
  683.     Tk_FreeOptions(configSpecs, (char *) scrollPtr, scrollPtr->display, 0);
  684.     ckfree((char *) scrollPtr);
  685. }
  686.  
  687. /*
  688.  *----------------------------------------------------------------------
  689.  *
  690.  * ConfigureScrollbar --
  691.  *
  692.  *    This procedure is called to process an argv/argc list, plus
  693.  *    the Tk option database, in order to configure (or
  694.  *    reconfigure) a scrollbar widget.
  695.  *
  696.  * Results:
  697.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  698.  *    returned, then interp->result contains an error message.
  699.  *
  700.  * Side effects:
  701.  *    Configuration information, such as colors, border width,
  702.  *    etc. get set for scrollPtr;  old resources get freed,
  703.  *    if there were any.
  704.  *
  705.  *----------------------------------------------------------------------
  706.  */
  707.  
  708. static int
  709. ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
  710.     Tcl_Interp *interp;            /* Used for error reporting. */
  711.     register Scrollbar *scrollPtr;    /* Information about widget;  may or
  712.                      * may not already have values for
  713.                      * some fields. */
  714.     int argc;                /* Number of valid entries in argv. */
  715.     char **argv;            /* Arguments. */
  716.     int flags;                /* Flags to pass to
  717.                      * Tk_ConfigureWidget. */
  718. {
  719.     size_t length;
  720.     XGCValues gcValues;
  721.     GC new;
  722.  
  723.     if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, configSpecs,
  724.         argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
  725.     return TCL_ERROR;
  726.     }
  727.  
  728.     /*
  729.      * A few options need special processing, such as parsing the
  730.      * orientation or setting the background from a 3-D border.
  731.      */
  732.  
  733.     length = strlen(scrollPtr->orientUid);
  734.     if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
  735.     scrollPtr->vertical = 1;
  736.     } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
  737.     scrollPtr->vertical = 0;
  738.     } else {
  739.     Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
  740.         "\": must be vertical or horizontal", (char *) NULL);
  741.     return TCL_ERROR;
  742.     }
  743.  
  744.     if (scrollPtr->command != NULL) {
  745.     scrollPtr->commandSize = strlen(scrollPtr->command);
  746.     } else {
  747.     scrollPtr->commandSize = 0;
  748.     }
  749.  
  750.     Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
  751.  
  752.     gcValues.foreground = scrollPtr->troughColorPtr->pixel;
  753.     new = Tk_GetGC(scrollPtr->tkwin, GCForeground, &gcValues);
  754.     if (scrollPtr->troughGC != None) {
  755.     Tk_FreeGC(scrollPtr->display, scrollPtr->troughGC);
  756.     }
  757.     scrollPtr->troughGC = new;
  758.     if (scrollPtr->copyGC == None) {
  759.     gcValues.graphics_exposures = False;
  760.     scrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
  761.         &gcValues);
  762.     }
  763.  
  764.     /*
  765.      * Register the desired geometry for the window (leave enough space
  766.      * for the two arrows plus a minimum-size slider, plus border around
  767.      * the whole window, if any).  Then arrange for the window to be
  768.      * redisplayed.
  769.      */
  770.  
  771.     ComputeScrollbarGeometry(scrollPtr);
  772.     EventuallyRedraw(scrollPtr);
  773.     return TCL_OK;
  774. }
  775.  
  776. /*
  777.  *--------------------------------------------------------------
  778.  *
  779.  * DisplayScrollbar --
  780.  *
  781.  *    This procedure redraws the contents of a scrollbar window.
  782.  *    It is invoked as a do-when-idle handler, so it only runs
  783.  *    when there's nothing else for the application to do.
  784.  *
  785.  * Results:
  786.  *    None.
  787.  *
  788.  * Side effects:
  789.  *    Information appears on the screen.
  790.  *
  791.  *--------------------------------------------------------------
  792.  */
  793.  
  794. static void
  795. DisplayScrollbar(clientData)
  796.     ClientData clientData;    /* Information about window. */
  797. {
  798.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  799.     register Tk_Window tkwin = scrollPtr->tkwin;
  800.     XPoint points[7];
  801.     Tk_3DBorder border;
  802.     int relief, width, elementBorderWidth;
  803.     Pixmap pixmap;
  804.  
  805.     if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  806.     goto done;
  807.     }
  808.  
  809.     if (scrollPtr->vertical) {
  810.     width = Tk_Width(tkwin) - 2*scrollPtr->inset;
  811.     } else {
  812.     width = Tk_Height(tkwin) - 2*scrollPtr->inset;
  813.     }
  814.     elementBorderWidth = scrollPtr->elementBorderWidth;
  815.     if (elementBorderWidth < 0) {
  816.     elementBorderWidth = scrollPtr->borderWidth;
  817.     }
  818.  
  819.     /*
  820.      * In order to avoid screen flashes, this procedure redraws
  821.      * the scrollbar in a pixmap, then copies the pixmap to the
  822.      * screen in a single operation.  This means that there's no
  823.      * point in time where the on-sreen image has been cleared.
  824.      */
  825.  
  826.     pixmap = Tk_GetPixmap(scrollPtr->display, Tk_WindowId(tkwin),
  827.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  828.  
  829.     if (scrollPtr->highlightWidth != 0) {
  830.     GC gc;
  831.  
  832.     if (scrollPtr->flags & GOT_FOCUS) {
  833.         gc = Tk_GCForColor(scrollPtr->highlightColorPtr, pixmap);
  834.     } else {
  835.         gc = Tk_GCForColor(scrollPtr->highlightBgColorPtr, pixmap);
  836.     }
  837.     Tk_DrawFocusHighlight(tkwin, gc, scrollPtr->highlightWidth, pixmap);
  838.     }
  839.     Tk_Draw3DRectangle(tkwin, pixmap, scrollPtr->bgBorder,
  840.         scrollPtr->highlightWidth, scrollPtr->highlightWidth,
  841.         Tk_Width(tkwin) - 2*scrollPtr->highlightWidth,
  842.         Tk_Height(tkwin) - 2*scrollPtr->highlightWidth,
  843.         scrollPtr->borderWidth, scrollPtr->relief);
  844.     XFillRectangle(scrollPtr->display, pixmap, scrollPtr->troughGC,
  845.         scrollPtr->inset, scrollPtr->inset,
  846.         (unsigned) (Tk_Width(tkwin) - 2*scrollPtr->inset),
  847.         (unsigned) (Tk_Height(tkwin) - 2*scrollPtr->inset));
  848.  
  849.     /*
  850.      * Draw the top or left arrow.  The coordinates of the polygon
  851.      * points probably seem odd, but they were carefully chosen with
  852.      * respect to X's rules for filling polygons.  These point choices
  853.      * cause the arrows to just fill the narrow dimension of the
  854.      * scrollbar and be properly centered.
  855.      */
  856.  
  857.     if (scrollPtr->activeField == TOP_ARROW) {
  858.     border = scrollPtr->activeBorder;
  859.     relief = scrollPtr->activeField == TOP_ARROW ? scrollPtr->activeRelief
  860.         : TK_RELIEF_RAISED;
  861.     } else {
  862.     border = scrollPtr->bgBorder;
  863.     relief = TK_RELIEF_RAISED;
  864.     }
  865.     if (scrollPtr->vertical) {
  866.     points[0].x = scrollPtr->inset - 1;
  867.     points[0].y = scrollPtr->arrowLength + scrollPtr->inset - 1;
  868.     points[1].x = width + scrollPtr->inset;
  869.     points[1].y = points[0].y;
  870.     points[2].x = width/2 + scrollPtr->inset;
  871.     points[2].y = scrollPtr->inset - 1;
  872.     Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
  873.         elementBorderWidth, relief);
  874.     } else {
  875.     points[0].x = scrollPtr->arrowLength + scrollPtr->inset - 1;
  876.     points[0].y = scrollPtr->inset - 1;
  877.     points[1].x = scrollPtr->inset;
  878.     points[1].y = width/2 + scrollPtr->inset;
  879.     points[2].x = points[0].x;
  880.     points[2].y = width + scrollPtr->inset;
  881.     Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
  882.         elementBorderWidth, relief);
  883.     }
  884.  
  885.     /*
  886.      * Display the bottom or right arrow.
  887.      */
  888.  
  889.     if (scrollPtr->activeField == BOTTOM_ARROW) {
  890.     border = scrollPtr->activeBorder;
  891.     relief = scrollPtr->activeField == BOTTOM_ARROW
  892.         ? scrollPtr->activeRelief : TK_RELIEF_RAISED;
  893.     } else {
  894.     border = scrollPtr->bgBorder;
  895.     relief = TK_RELIEF_RAISED;
  896.     }
  897.     if (scrollPtr->vertical) {
  898.     points[0].x = scrollPtr->inset;
  899.     points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength
  900.         - scrollPtr->inset + 1;
  901.     points[1].x = width/2 + scrollPtr->inset;
  902.     points[1].y = Tk_Height(tkwin) - scrollPtr->inset;
  903.     points[2].x = width + scrollPtr->inset;
  904.     points[2].y = points[0].y;
  905.     Tk_Fill3DPolygon(tkwin, pixmap, border,
  906.         points, 3, elementBorderWidth, relief);
  907.     } else {
  908.     points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength
  909.         - scrollPtr->inset + 1;
  910.     points[0].y = scrollPtr->inset - 1;
  911.     points[1].x = points[0].x;
  912.     points[1].y = width + scrollPtr->inset;
  913.     points[2].x = Tk_Width(tkwin) - scrollPtr->inset;
  914.     points[2].y = width/2 + scrollPtr->inset;
  915.     Tk_Fill3DPolygon(tkwin, pixmap, border,
  916.         points, 3, elementBorderWidth, relief);
  917.     }
  918.  
  919.     /*
  920.      * Display the slider.
  921.      */
  922.  
  923.     if (scrollPtr->activeField == SLIDER) {
  924.     border = scrollPtr->activeBorder;
  925.     relief = scrollPtr->activeField == SLIDER ? scrollPtr->activeRelief
  926.         : TK_RELIEF_RAISED;
  927.     } else {
  928.     border = scrollPtr->bgBorder;
  929.     relief = TK_RELIEF_RAISED;
  930.     }
  931.     if (scrollPtr->vertical) {
  932.     Tk_Fill3DRectangle(tkwin, pixmap, border,
  933.         scrollPtr->inset, scrollPtr->sliderFirst,
  934.         width, scrollPtr->sliderLast - scrollPtr->sliderFirst,
  935.         elementBorderWidth, relief);
  936.     } else {
  937.     Tk_Fill3DRectangle(tkwin, pixmap, border,
  938.         scrollPtr->sliderFirst, scrollPtr->inset,
  939.         scrollPtr->sliderLast - scrollPtr->sliderFirst, width,
  940.         elementBorderWidth, relief);
  941.     }
  942.  
  943.     /*
  944.      * Copy the information from the off-screen pixmap onto the screen,
  945.      * then delete the pixmap.
  946.      */
  947.  
  948.     XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin),
  949.         scrollPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  950.         (unsigned) Tk_Height(tkwin), 0, 0);
  951.     Tk_FreePixmap(scrollPtr->display, pixmap);
  952.  
  953.     done:
  954.     scrollPtr->flags &= ~REDRAW_PENDING;
  955. }
  956.  
  957. /*
  958.  *--------------------------------------------------------------
  959.  *
  960.  * ScrollbarEventProc --
  961.  *
  962.  *    This procedure is invoked by the Tk dispatcher for various
  963.  *    events on scrollbars.
  964.  *
  965.  * Results:
  966.  *    None.
  967.  *
  968.  * Side effects:
  969.  *    When the window gets deleted, internal structures get
  970.  *    cleaned up.  When it gets exposed, it is redisplayed.
  971.  *
  972.  *--------------------------------------------------------------
  973.  */
  974.  
  975. static void
  976. ScrollbarEventProc(clientData, eventPtr)
  977.     ClientData clientData;    /* Information about window. */
  978.     XEvent *eventPtr;        /* Information about event. */
  979. {
  980.     Scrollbar *scrollPtr = (Scrollbar *) clientData;
  981.  
  982.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  983.     EventuallyRedraw(scrollPtr);
  984.     } else if (eventPtr->type == DestroyNotify) {
  985.     if (scrollPtr->tkwin != NULL) {
  986.         scrollPtr->tkwin = NULL;
  987.         Tcl_DeleteCommand(scrollPtr->interp,
  988.             Tcl_GetCommandName(scrollPtr->interp,
  989.             scrollPtr->widgetCmd));
  990.     }
  991.     if (scrollPtr->flags & REDRAW_PENDING) {
  992.         Tk_CancelIdleCall(DisplayScrollbar, (ClientData) scrollPtr);
  993.     }
  994.     Tk_EventuallyFree((ClientData) scrollPtr, DestroyScrollbar);
  995.     } else if (eventPtr->type == ConfigureNotify) {
  996.     ComputeScrollbarGeometry(scrollPtr);
  997.     EventuallyRedraw(scrollPtr);
  998.     } else if (eventPtr->type == FocusIn) {
  999.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1000.         scrollPtr->flags |= GOT_FOCUS;
  1001.         if (scrollPtr->highlightWidth > 0) {
  1002.         EventuallyRedraw(scrollPtr);
  1003.         }
  1004.     }
  1005.     } else if (eventPtr->type == FocusOut) {
  1006.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1007.         scrollPtr->flags &= ~GOT_FOCUS;
  1008.         if (scrollPtr->highlightWidth > 0) {
  1009.         EventuallyRedraw(scrollPtr);
  1010.         }
  1011.     }
  1012.     }
  1013. }
  1014.  
  1015. /*
  1016.  *----------------------------------------------------------------------
  1017.  *
  1018.  * ScrollbarCmdDeletedProc --
  1019.  *
  1020.  *    This procedure is invoked when a widget command is deleted.  If
  1021.  *    the widget isn't already in the process of being destroyed,
  1022.  *    this command destroys it.
  1023.  *
  1024.  * Results:
  1025.  *    None.
  1026.  *
  1027.  * Side effects:
  1028.  *    The widget is destroyed.
  1029.  *
  1030.  *----------------------------------------------------------------------
  1031.  */
  1032.  
  1033. static void
  1034. ScrollbarCmdDeletedProc(clientData)
  1035.     ClientData clientData;    /* Pointer to widget record for widget. */
  1036. {
  1037.     Scrollbar *scrollPtr = (Scrollbar *) clientData;
  1038.     Tk_Window tkwin = scrollPtr->tkwin;
  1039.  
  1040.     /*
  1041.      * This procedure could be invoked either because the window was
  1042.      * destroyed and the command was then deleted (in which case tkwin
  1043.      * is NULL) or because the command was deleted, and then this procedure
  1044.      * destroys the widget.
  1045.      */
  1046.  
  1047.     if (tkwin != NULL) {
  1048.     scrollPtr->tkwin = NULL;
  1049.     Tk_DestroyWindow(tkwin);
  1050.     }
  1051. }
  1052.  
  1053. /*
  1054.  *----------------------------------------------------------------------
  1055.  *
  1056.  * ComputeScrollbarGeometry --
  1057.  *
  1058.  *    After changes in a scrollbar's size or configuration, this
  1059.  *    procedure recomputes various geometry information used in
  1060.  *    displaying the scrollbar.
  1061.  *
  1062.  * Results:
  1063.  *    None.
  1064.  *
  1065.  * Side effects:
  1066.  *    The scrollbar will be displayed differently.
  1067.  *
  1068.  *----------------------------------------------------------------------
  1069.  */
  1070.  
  1071. static void
  1072. ComputeScrollbarGeometry(scrollPtr)
  1073.     register Scrollbar *scrollPtr;    /* Scrollbar whose geometry may
  1074.                      * have changed. */
  1075. {
  1076.     int width, fieldLength;
  1077.  
  1078.     if (scrollPtr->highlightWidth < 0) {
  1079.     scrollPtr->highlightWidth = 0;
  1080.     }
  1081.     scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth;
  1082.     width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
  1083.         : Tk_Height(scrollPtr->tkwin);
  1084.     scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1;
  1085.     fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
  1086.         : Tk_Width(scrollPtr->tkwin))
  1087.         - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  1088.     if (fieldLength < 0) {
  1089.     fieldLength = 0;
  1090.     }
  1091.     scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction;
  1092.     scrollPtr->sliderLast = fieldLength*scrollPtr->lastFraction;
  1093.  
  1094.     /*
  1095.      * Adjust the slider so that some piece of it is always
  1096.      * displayed in the scrollbar and so that it has at least
  1097.      * a minimal width (so it can be grabbed with the mouse).
  1098.      */
  1099.  
  1100.     if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) {
  1101.     scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
  1102.     }
  1103.     if (scrollPtr->sliderFirst < 0) {
  1104.     scrollPtr->sliderFirst = 0;
  1105.     }
  1106.     if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
  1107.         + MIN_SLIDER_LENGTH)) {
  1108.     scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
  1109.     }
  1110.     if (scrollPtr->sliderLast > fieldLength) {
  1111.     scrollPtr->sliderLast = fieldLength;
  1112.     }
  1113.     scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset;
  1114.     scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset;
  1115.  
  1116.     /*
  1117.      * Register the desired geometry for the window (leave enough space
  1118.      * for the two arrows plus a minimum-size slider, plus border around
  1119.      * the whole window, if any).  Then arrange for the window to be
  1120.      * redisplayed.
  1121.      */
  1122.  
  1123.     if (scrollPtr->vertical) {
  1124.     Tk_GeometryRequest(scrollPtr->tkwin,
  1125.         scrollPtr->width + 2*scrollPtr->inset,
  1126.         2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  1127.         + scrollPtr->inset));
  1128.     } else {
  1129.     Tk_GeometryRequest(scrollPtr->tkwin,
  1130.         2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  1131.         + scrollPtr->inset), scrollPtr->width + 2*scrollPtr->inset);
  1132.     }
  1133.     Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset);
  1134. }
  1135.  
  1136. /*
  1137.  *--------------------------------------------------------------
  1138.  *
  1139.  * ScrollbarPosition --
  1140.  *
  1141.  *    Determine the scrollbar element corresponding to a
  1142.  *    given position.
  1143.  *
  1144.  * Results:
  1145.  *    One of TOP_ARROW, TOP_GAP, etc., indicating which element
  1146.  *    of the scrollbar covers the position given by (x, y).  If
  1147.  *    (x,y) is outside the scrollbar entirely, then OUTSIDE is
  1148.  *    returned.
  1149.  *
  1150.  * Side effects:
  1151.  *    None.
  1152.  *
  1153.  *--------------------------------------------------------------
  1154.  */
  1155.  
  1156. static int
  1157. ScrollbarPosition(scrollPtr, x, y)
  1158.     register Scrollbar *scrollPtr;    /* Scrollbar widget record. */
  1159.     int x, y;                /* Coordinates within scrollPtr's
  1160.                      * window. */
  1161. {
  1162.     int length, width, tmp;
  1163.  
  1164.     if (scrollPtr->vertical) {
  1165.     length = Tk_Height(scrollPtr->tkwin);
  1166.     width = Tk_Width(scrollPtr->tkwin);
  1167.     } else {
  1168.     tmp = x;
  1169.     x = y;
  1170.     y = tmp;
  1171.     length = Tk_Width(scrollPtr->tkwin);
  1172.     width = Tk_Height(scrollPtr->tkwin);
  1173.     }
  1174.  
  1175.     if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset))
  1176.         || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) {
  1177.     return OUTSIDE;
  1178.     }
  1179.  
  1180.     /*
  1181.      * All of the calculations in this procedure mirror those in
  1182.      * DisplayScrollbar.  Be sure to keep the two consistent.
  1183.      */
  1184.  
  1185.     if (y < (scrollPtr->inset + scrollPtr->arrowLength)) {
  1186.     return TOP_ARROW;
  1187.     }
  1188.     if (y < scrollPtr->sliderFirst) {
  1189.     return TOP_GAP;
  1190.     }
  1191.     if (y < scrollPtr->sliderLast) {
  1192.     return SLIDER;
  1193.     }
  1194.     if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) {
  1195.     return BOTTOM_ARROW;
  1196.     }
  1197.     return BOTTOM_GAP;
  1198. }
  1199.  
  1200. /*
  1201.  *--------------------------------------------------------------
  1202.  *
  1203.  * EventuallyRedraw --
  1204.  *
  1205.  *    Arrange for one or more of the fields of a scrollbar
  1206.  *    to be redrawn.
  1207.  *
  1208.  * Results:
  1209.  *    None.
  1210.  *
  1211.  * Side effects:
  1212.  *    None.
  1213.  *
  1214.  *--------------------------------------------------------------
  1215.  */
  1216.  
  1217. static void
  1218. EventuallyRedraw(scrollPtr)
  1219.     register Scrollbar *scrollPtr;    /* Information about widget. */
  1220. {
  1221.     if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
  1222.     return;
  1223.     }
  1224.     if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
  1225.     Tk_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr);
  1226.     scrollPtr->flags |= REDRAW_PENDING;
  1227.     }
  1228. }
  1229.